#ifndef _NE_KEYBOARD_H_
#define _NE_KEYBOARD_H_

#include <neinit.h>

namespace ne
{
    enum KeyboardModifier
    {
        KM_NONE         = 0x00,
        KM_SHIFT        = 0x01,
        KM_CTRL         = 0x02,
        KM_ALT          = 0x04
    };

    enum KeyboardTranslationMode
    {
        KTM_OFF         = 0x00,
        KTM_ASCII       = 0x01,
        KTM_UNICODE     = 0x02
    };

    enum KeyboardKey
    {
        KK_UNASSIGNED   = 0x00,
        KK_ESC          = 0x01,    // Escape
        KK_1            = 0x02,    // 1! on main keyboard exclam
        KK_2            = 0x03,    // 2@ on main keyboard at
        KK_3            = 0x04,    // 3# on main keyboard numbersign
        KK_4            = 0x05,    // 4$ on main keyboard dollar
        KK_5            = 0x06,    // 5% on main keyboard percent
        KK_6            = 0x07,    // 6^ on main keyboard caret
        KK_7            = 0x08,    // 7& on main keyboard ampersand
        KK_8            = 0x09,    // 8* on main keyboard asterisk
        KK_9            = 0x0A,    // 9( on main keyboard parenleft
        KK_0            = 0x0B,    // 0) on main keyboard parenright
        KK_UNDER        = 0x0C,    // _- on main keyboard underscore/minus
        KK_EQUAL        = 0x0D,    // =+ on main keyboard equal/plus
        KK_BACK         = 0x0E,    // Backspace
        KK_TAB          = 0x0F,    // Tab
        KK_Q            = 0x10,
        KK_W            = 0x11,
        KK_E            = 0x12,
        KK_R            = 0x13,
        KK_T            = 0x14,
        KK_Y            = 0x15,
        KK_U            = 0x16,
        KK_I            = 0x17,
        KK_O            = 0x18,
        KK_P            = 0x19,
        KK_LBRACKET     = 0x1A,    // [{ on main keyboard bracketleft/braceleft
        KK_RBRACKET     = 0x1B,    // ]} on main keyboard bracketright/braceright
        KK_RETURN       = 0x1C,    // Enter on main keyboard
        KK_LCONTROL     = 0x1D,    // Left ctrl
        KK_A            = 0x1E,
        KK_S            = 0x1F,
        KK_D            = 0x20,
        KK_F            = 0x21,
        KK_G            = 0x22,
        KK_H            = 0x23,
        KK_J            = 0x24,
        KK_K            = 0x25,
        KK_L            = 0x26,
        KK_SEMICOLON    = 0x27,    // ;: on main keyboard semicolon/colon
        KK_APOSTROPHE   = 0x28,    // '" on main keyboard quote/doublequote
        KK_GRAVE        = 0x29,    // `~ on main keyboard backquote/tilde
        KK_LSHIFT       = 0x2A,    // Left shift
        KK_BACKSLASH    = 0x2B,    // \| on main keyboard backflash/bar
        KK_Z            = 0x2C,
        KK_X            = 0x2D,
        KK_C            = 0x2E,
        KK_V            = 0x2F,
        KK_B            = 0x30,
        KK_N            = 0x31,
        KK_M            = 0x32,
        KK_COMMA        = 0x33,    // ,< on main keyboard comma/less
        KK_PERIOD       = 0x34,    // .> on main keyboard period/greater
        KK_SLASH        = 0x35,    // /? on main keyboard slash/question
        KK_RSHIFT       = 0x36,    // Right shift
        KK_MULTIPLY     = 0x37,    // * on numeric keypad
        KK_LMENU        = 0x38,    // Left Alt
        KK_SPACE        = 0x39,    // Space
        KK_CAPITAL      = 0x3A,    // Capital lock
        KK_F1           = 0x3B,
        KK_F2           = 0x3C,
        KK_F3           = 0x3D,
        KK_F4           = 0x3E,
        KK_F5           = 0x3F,
        KK_F6           = 0x40,
        KK_F7           = 0x41,
        KK_F8           = 0x42,
        KK_F9           = 0x43,
        KK_F10          = 0x44,
        KK_NUMLOCK      = 0x45,    // Number lock
        KK_SCROLL       = 0x46,    // Scroll Lock
        KK_NUMPAD7      = 0x47,
        KK_NUMPAD8      = 0x48,
        KK_NUMPAD9      = 0x49,
        KK_MINUS        = 0x4A,    // - on numeric keypad
        KK_NUMPAD4      = 0x4B,
        KK_NUMPAD5      = 0x4C,
        KK_NUMPAD6      = 0x4D,
        KK_PLUS         = 0x4E,    // + on numeric keypad
        KK_NUMPAD1      = 0x4F,
        KK_NUMPAD2      = 0x50,
        KK_NUMPAD3      = 0x51,
        KK_NUMPAD0      = 0x52,
        KK_DECIMAL      = 0x53,    // . on numeric keypad
        KK_OEM_102      = 0x56,    // < > | on UK/Germany keyboards
        KK_F11          = 0x57,
        KK_F12          = 0x58,
        KK_F13          = 0x64,    //                     (NEC PC98)
        KK_F14          = 0x65,    //                     (NEC PC98)
        KK_F15          = 0x66,    //                     (NEC PC98)
        KK_KANA         = 0x70,    // (Japanese keyboard)
        KK_ABNT_C1      = 0x73,    // / ? on Portugese (Brazilian) keyboards
        KK_CONVERT      = 0x79,    // (Japanese keyboard)
        KK_NOCONVERT    = 0x7B,    // (Japanese keyboard)
        KK_YEN          = 0x7D,    // (Japanese keyboard)
        KK_ABNT_C2      = 0x7E,    // Numpad . on Portugese (Brazilian) keyboards
        KK_NUMPADEQUALS = 0x8D,    // = on numeric keypad (NEC PC98)
        KK_PREVTRACK    = 0x90,    // Previous Track (KK_CIRCUMFLEX on Japanese keyboard)
        KK_AT           = 0x91,    //                     (NEC PC98)
        KK_COLON        = 0x92,    //                     (NEC PC98)
        KK_UNDERLINE    = 0x93,    //                     (NEC PC98)
        KK_KANJI        = 0x94,    // (Japanese keyboard)
        KK_STOP         = 0x95,    //                     (NEC PC98)
        KK_AX           = 0x96,    //                     (Japan AX)
        KK_UNLABELED    = 0x97,    //                        (J3100)
        KK_NEXTTRACK    = 0x99,    // Next Track
        KK_NUMPADENTER  = 0x9C,    // Enter on numeric keypad
        KK_RCONTROL     = 0x9D,    // Right ctrl
        KK_MUTE         = 0xA0,    // Mute
        KK_CALCULATOR   = 0xA1,    // Calculator
        KK_PLAYPAUSE    = 0xA2,    // Play / Pause
        KK_MEDIASTOP    = 0xA4,    // Media Stop
        KK_VOLUMEDOWN   = 0xAE,    // Volume -
        KK_VOLUMEUP     = 0xB0,    // Volume +
        KK_WEBHOME      = 0xB2,    // Web home
        KK_NUMPADCOMMA  = 0xB3,    // , on numeric keypad (NEC PC98)
        KK_DIVIDE       = 0xB5,    // / on numeric keypad
        KK_SYSRQ        = 0xB7,
        KK_RMENU        = 0xB8,    // Right Alt
        KK_PAUSE        = 0xC5,    // Pause
        KK_HOME         = 0xC7,    // Home on arrow keypad
        KK_UP           = 0xC8,    // UpArrow on arrow keypad
        KK_PGUP         = 0xC9,    // PgUp on arrow keypad
        KK_LEFT         = 0xCB,    // LeftArrow on arrow keypad
        KK_RIGHT        = 0xCD,    // RightArrow on arrow keypad
        KK_END          = 0xCF,    // End on arrow keypad
        KK_DOWN         = 0xD0,    // DownArrow on arrow keypad
        KK_PGDOWN       = 0xD1,    // PgDn on arrow keypad
        KK_INSERT       = 0xD2,    // Insert on arrow keypad
        KK_DELETE       = 0xD3,    // Delete on arrow keypad
        KK_LWIN         = 0xDB,    // Left Windows key
        KK_RWIN         = 0xDC,    // Right Windows key
        KK_APPS         = 0xDD,    // AppMenu key
        KK_POWER        = 0xDE,    // System Power
        KK_SLEEP        = 0xDF,    // System Sleep
        KK_WAKE         = 0xE3,    // System Wake
        KK_WEBSEARCH    = 0xE5,    // Web Search
        KK_WEBFAVORITES = 0xE6,    // Web Favorites
        KK_WEBREFRESH   = 0xE7,    // Web Refresh
        KK_WEBSTOP      = 0xE8,    // Web Stop
        KK_WEBFORWARD   = 0xE9,    // Web Forward
        KK_WEBBACK      = 0xEA,    // Web Back
        KK_MYCOMPUTER   = 0xEB,    // My Computer
        KK_MAIL         = 0xEC,    // Mail
        KK_MEDIASELECT  = 0xED     // Media Select
    };

    enum ASCII
    {
        ASCII_NUL = 0x00,    // Null CTRL-@
        ASCII_SOH = 0x01,    // Start of heading CTRL-A
        ASCII_STX = 0x02,    // Start of text CTRL-B
        ASCII_ETX = 0x03,    // End of text CTRL-C
        ASCII_EOT = 0x04,    // End of transmission CTRL-D
        ASCII_ENQ = 0x05,    // End of query CTRL-E
        ASCII_ACK = 0x06,    // Acknowledge CTRL-F
        ASCII_BEL = 0x07,    // Bell CTRL-G
        ASCII_BS  = 0x08,    // Backspace CTRL-H
        ASCII_HT  = 0x09,    // Horizontal tab CTRL-I
        ASCII_LF  = 0x0A,    // Line feed CTRL-J
        ASCII_VT  = 0x0B,    // Vertical tab CTRL-K
        ASCII_FF  = 0x0C,    // Form feed CTRL-L
        ASCII_CR  = 0x0D,    // Carriage return CTRL-M
        ASCII_SO  = 0x0E,    // Shift out CTRL-N
        ASCII_SI  = 0x0F,    // Shift in CTRL-O
        ASCII_DLE = 0x10,    // Data link escape CTRL-P
        ASCII_DC1 = 0x11,    // Device control 1 CTRL-Q
        ASCII_DC2 = 0x12,    // Device control 2 CTRL-R
        ASCII_DC3 = 0x13,    // Device control 3 CTRL-S
        ASCII_DC4 = 0x14,    // Device control 4 CTRL-T
        ASCII_NAK = 0x15,    // Negative acknowledge CTRL-U
        ASCII_SYN = 0x16,    // Synchronize CTRL-V
        ASCII_ETB = 0x17,    // End of transmission block CTRL-W
        ASCII_CAN = 0x18,    // Cancel CTRL-X
        ASCII_EM  = 0x19,    // End of medium CTRL-Y
        ASCII_SUB = 0x1A,    // Substitute CTRL-Z
        ASCII_ESC = 0x1B,    // Escape CTRL-[
        ASCII_FS  = 0x1C,    // File separator / Right arrow CTRL-( \ )
        ASCII_GS  = 0x1D,    // Group separator / Left arrow CTRL-]
        ASCII_RS  = 0x1E,    // Record separator / Up arrow CTRL-^
        ASCII_US  = 0x1F,    // Unit separator / Down arrow CTRL-_
        ASCII_SP  = 0x20,    // Space
        ASCII_DEL = 0x7F,    // Delete
    };

    class NEAPI Keyboard
    {
        public:
            Keyboard();

            void setModifierUp(const KeyboardModifier km);
            bool isModifierUp(const KeyboardModifier km) const;

            void setModifierDown(const KeyboardModifier km);
            bool isModifierDown(const KeyboardModifier km) const;

            uint32_t getModifier() const;

            void setTranslationMode(const KeyboardTranslationMode ktm);
            KeyboardTranslationMode getTranslationMode() const;

            void setKeyUp(const KeyboardKey key);
            bool isKeyUp(const KeyboardKey key) const;

            void setKeyDown(const KeyboardKey key);
            bool isKeyDown(const KeyboardKey key) const;

            void reset();

            ~Keyboard();

        private:
            Keyboard(const Keyboard &);

            Keyboard& operator = (const Keyboard &);

        public:
            static KeyboardKey ConvertASCIIKey(const uint32_t key);

        public:
            static const uint32_t KEYBOARD_KEYS;

        private:
            uint32_t mModifier;
            KeyboardTranslationMode mTranslationMode;
            bool mKeysDown[256];
    };
}

#endif
